﻿using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using log4net;

namespace Mailer.Utilities
{
	public class Ripper
	{
		static private ILog logger = LogManager.GetLogger(typeof(Ripper));

		private static string GetContentTypeCharset(string contenttypevalue, string defaultvalue)
		{
			var m = Regex.Match(contenttypevalue, "charset=([^\\s$]+)", RegexOptions.Singleline | RegexOptions.IgnoreCase);
			if (m.Success)
				return m.Groups[1].Value;

			return defaultvalue;
		}

		private static string GetContentType(string contenttypevalue, string defaultvalue)
		{
			var m = Regex.Match(contenttypevalue, "^([^;$]+)", RegexOptions.Singleline | RegexOptions.IgnoreCase);
			if (m.Success)
				return m.Groups[1].Value;

			return defaultvalue;
		}

		private static string GetTagAttribute(string htmltag, string attributename, string defaultvalue)
		{
			var m = Regex.Match(htmltag, Regex.Escape(attributename) + "[\\s]*=[\\s]*\\\"([^\\\"]+)", RegexOptions.Singleline | RegexOptions.IgnoreCase);
			if (m.Success)
				return m.Groups[1].Value;

			return defaultvalue;
		}

		public static string TryRip(string url)
		{
			return TryRip(url, Encoding.UTF8);
		}

		// all instead of WebClient.DownloadString ... which doesnt work as supposed.

		public static string TryRip(string url, Encoding fallbackencoding)
		{
			logger.Info("TryRip \"" + url + "\"");

			if (!Uri.IsWellFormedUriString(url, UriKind.Absolute))
				return "";

			var wc = new WebClient();

			byte[] bytes = null;
			try
			{
				bytes = wc.DownloadData(url);
			}
			catch (Exception zz)
			{
				logger.Error("Download failed", zz);
			}

			if (bytes == null || bytes.Length == 0)
				return "";

			logger.Info("Got " + bytes.Length + " bytes");

			Encoding enc = null;

			var contenttype = "text/plain";

			foreach (var h in wc.ResponseHeaders)
			{
				var v = wc.ResponseHeaders[h.ToString()];

				logger.Debug("Got header \"" + h + "\" with value \"" + v + "\"");
				if (!h.ToString().Equals("content-type", StringComparison.InvariantCultureIgnoreCase))
					continue;

				logger.Info("Got contenttype header: \"" + v + "\"");

				contenttype = GetContentType(v, contenttype);

				logger.Info("Got contenttype: \"" + contenttype + "\"");

				var encstr = GetContentTypeCharset(v, "");
				if (String.IsNullOrEmpty(encstr))
					continue;

				logger.Debug("Got charset-string: \"" + encstr + "\"");


				try
				{
					enc = Encoding.GetEncoding(encstr);
				}
				catch (Exception)
				{
					logger.Warn("No such encoding: " + encstr);
				}
			}

			if (enc == null)
			{
				logger.Debug("No encoding header found, checking content...");

				string testhtml = "";
				try
				{
					testhtml = Encoding.UTF8.GetString(bytes);
				}
				catch (Exception zz)
				{
					logger.Error("Failed to decode string for testing: ", zz);
				}

				if (contenttype.Equals("text/html", StringComparison.InvariantCultureIgnoreCase))
				{
					// <META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET="windows-1252">
					// <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

					var mc = Regex.Matches(testhtml, "<meta ([^>]+)>", RegexOptions.Multiline | RegexOptions.IgnoreCase);
					foreach (var m in mc)
					{
						var metacode = m.ToString();
						var httpequiv = GetTagAttribute(metacode, "http-equiv", "");
						var metacontent = GetTagAttribute(metacode, "content", "");
						
						logger.Debug("Found meta: " + metacode);
						logger.Debug("Found httpequiv: " + httpequiv);
						logger.Debug("Found metacontent: " + metacontent);

						if (!httpequiv.Equals("content-type", StringComparison.InvariantCultureIgnoreCase))
							continue;

						logger.Info("Got meta contenttype: \"" + metacontent + "\"");

						var encstr = GetContentTypeCharset(metacontent, "");
						if (String.IsNullOrEmpty(encstr))
							continue;

						logger.Debug("Got meta charset-string: \"" + encstr + "\"");
						
						try
						{
							enc = Encoding.GetEncoding(encstr);
						}
						catch (Exception)
						{
							logger.Warn("No such encoding: " + encstr);
						}
						
					}
				}

			}

			if (enc == null)
			{
				logger.Debug("Using fallback encoding: " + fallbackencoding);
				enc = fallbackencoding;
			}

			logger.Debug("Using encoding: " + enc);

			string ret = "";
			try
			{
				ret = enc.GetString(bytes);
			}
			catch (Exception zz)
			{
				logger.Error("Failed to decode string: ", zz);
			}

			return ret;
		}
	}
}
